#include "pch.h"
#include "SurfaceNets.h"

namespace mini
{

	CSurfaceNets::CSurfaceNets()
	{
		voxelCornerOffsets.resize(8);
		voxelCornerOffsets[0] = CPoint3F(0, 0, 0);	// 0
		voxelCornerOffsets[1] = CPoint3F(1, 0, 0); 	// 1
		voxelCornerOffsets[2] = CPoint3F(0, 1, 0); 	// 2
		voxelCornerOffsets[3] = CPoint3F(1, 1, 0); 	// 3
		voxelCornerOffsets[4] = CPoint3F(0, 0, 1); 	// 4
		voxelCornerOffsets[5] = CPoint3F(1, 0, 1); 	// 5
		voxelCornerOffsets[6] = CPoint3F(0, 1, 1); 	// 6
		voxelCornerOffsets[7] = CPoint3F(1, 1, 1); 	// 7
	}

	CSurfaceNets::~CSurfaceNets()
	{

	}

	CPoint3F CSurfaceNets::sampleResolution()
	{
		auto samples = sampleSpace.size() * samplesPerMeter;
		return CPoint3F(int(samples.x), int(samples.y), int(samples.z));
	}

	void CSurfaceNets::GenerateIntersectionTable()
	{
		for (int i = 0; i < 256; ++i)
		{
			int em = 0;
			for (int j = 0; j < 24; j += 2)
			{
				int a = i & (1 << cubeEdges[j]);
				int b = i & (1 << cubeEdges[j + 1]);
				em |= a != b ? (1 << (j >> 1)) : 0;
			}
			edgeTable[i] = em;
		}
	}

	void CSurfaceNets::GenerateCubeEdgesTable()
	{
		int k = 0;
		for (int i = 0; i < 8; ++i)
		{
			for (int j = 1; j <= 4; j <<= 1)
			{
				int p = i ^ j;
				if (i <= p)
				{
					cubeEdges[k++] = i;
					cubeEdges[k++] = p;
				}
			}
		}
	}

	void CSurfaceNets::runSurfaceNets()
	{
		//std::vector<int> buffer(4096);
		//std::vector<CPoint3F> vertices;
		//std::vector<CPoint4I> faces;
		//int n = 0;
		//std::vector<int> x(3);
		//, R = new Int32Array([1, (dims[0] + 1), (dims[0] + 1) * (dims[1] + 1)]);
		//std::vector<float> grid(8);
		//int buf_no = 1;

		////Resize buffer if necessary 
		//if (R[2] * 2 > buffer.size())
		//{
		//	buffer.resize(buffer.size() * 2);
		//}
		////March over the voxel grid
		//for (x[2] = 0; x[2] < dims[2] - 1; ++x[2], n += dims[0], buf_no ^= 1, R[2] = -R[2])
		//{
		//	//m is the pointer into the buffer we are going to use.  
		//	   //This is slightly obtuse because javascript does not have good support for packed data structures, so we must use typed arrays :(
		//	   //The contents of the buffer will be the indices of the vertices on the previous x/y slice of the volume
		//	int m = 1 + (dims[0] + 1) * (1 + buf_no * (dims[1] + 1));

		//	for (x[1] = 0; x[1] < dims[1] - 1; ++x[1], ++n, m += 2)
		//	{
		//		for (x[0] = 0; x[0] < dims[0] - 1; ++x[0], ++n, ++m)
		//		{

		//			//Read in 8 field values around this vertex and store them in an array
		//			//Also calculate 8-bit mask, like in marching cubes, so we can speed up sign checks later
		//			int mask = 0, g = 0, idx = n;
		//			for (int k = 0; k < 2; ++k, idx += dims[0] * (dims[1] - 2))
		//			{
		//				for (int j = 0; j < 2; ++j, idx += dims[0] - 2)
		//				{
		//					for (int i = 0; i < 2; ++i, ++g, ++idx)
		//					{

		//						int p = data[idx];
		//						grid[g] = p;
		//						mask |= (p < 0) ? (1 << g) : 0;
		//					}
		//				}
		//			}
		//			//Check for early termination if cell does not intersect boundary
		//			if (mask == 0 || mask == 0xff)
		//			{
		//				continue;
		//			}

		//			//Sum up edge intersections
		//			int edge_mask = edgeTable[mask];
		//			CPoint3F v(0.0, 0.0, 0.0);
		//			int e_count = 0;

		//			for (int i = 0; i < 12; ++i)
		//			{
		//				//Use edge mask to check if it is crossed
		//				if (!(edge_mask & (1 << i)))
		//				{
		//					continue;
		//				}

		//				//If it did, increment number of edge crossings
		//				++e_count;

		//				//Now find the point of intersection
		//				int e0 = cubeEdges[i << 1];       //Unpack vertices
		//				int e1 = cubeEdges[(i << 1) + 1];
		//				float g0 = grid[e0];                 //Unpack grid values
		//				float g1 = grid[e1];
		//				float t = g0 - g1;                 //Compute point of intersection
		//				if (fabs(t) > 1e-6)
		//				{
		//					t = g0 / t;
		//				}
		//				else
		//				{
		//					continue;
		//				}
		//				//Interpolate vertices and add up intersections (this can be done without multiplying)
		//				for (int j = 0, k = 1; j < 3; ++j, k <<= 1)
		//				{
		//					int a = e0 & k;
		//					int b = e1 & k;
		//					if (a != b)
		//					{
		//						v[j] += a ? 1.0 - t : t;
		//					}
		//					else
		//					{
		//						v[j] += a ? 1.0 : 0;
		//					}
		//				}
		//			}

		//			//Now we just average the edge intersections and add them to coordinate
		//			float s = 1.0f / e_count;
		//			for (int i = 0; i < 3; ++i)
		//			{
		//				v[i] = x[i] + s * v[i];
		//			}
		//			//Add vertex to buffer, store pointer to vertex index in buffer
		//			buffer[m] = vertices.size();
		//			vertices.push_back(v);

		//			//Now we need to add faces together, to do this we just loop over 3 basis components
		//			for (int i = 0; i < 3; ++i)
		//			{
		//				//The first three entries of the edge_mask count the crossings along the edge
		//				if (!(edge_mask & (1 << i)))
		//				{
		//					continue;
		//				}

		//				// i = axes we are point along.  iu, iv = orthogonal axes
		//				int iu = (i + 1) % 3;
		//				int iv = (i + 2) % 3;

		//				//If we are on a boundary, skip it
		//				if (x[iu] == 0 || x[iv] == 0)
		//				{
		//					continue;
		//				}

		//				//Otherwise, look up adjacent edges in buffer
		//				int du = R[iu];
		//				int dv = R[iv];

		//				//Remember to flip orientation depending on the sign of the corner.
		//				if (mask & 1)
		//				{
		//					faces.push_back(CPoint4I([buffer[m], buffer[m - du], buffer[m - du - dv], buffer[m - dv]]));
		//				}
		//				else
		//				{
		//					faces.push_back(CPoint4I([buffer[m], buffer[m - dv], buffer[m - du - dv], buffer[m - du]]));
		//				}
		//			}

		//		}
		//	}
		//}
	}

	void CSurfaceNets::Awake()
	{	
		auto v = sampleResolution();
		voxels.resize(v.x);

		for (int x = 0; x < v.x; ++x)
		{
			voxels[x].resize(v.y);
			for (int y = 0; y < v.y; ++y)
			{
				voxels[x][y].resize(v.z);
			}
		}

		edgeTable.resize(256);
		cubeEdges.resize(24);

		int k = 0;
		for (int i = 0; i < 8; ++i)
		{
			for (int j = 1; j <= 4; j <<= 1)
			{
				int p = i ^ j;
				if (i <= p)
				{
					cubeEdges[k++] = i;
					cubeEdges[k++] = p;
				}
			}
		}

		for (int i = 0; i < 256; ++i)
		{
			int em = 0;
			for (int j = 0; j < 24; j += 2)
			{
				int a = i & (1 << cubeEdges[j]);
				int b = i & (1 << cubeEdges[j + 1]);
				em |= a != b ? (1 << (j >> 1)) : 0;
			}
			edgeTable[i] = em;
		}


		runSurfaceNets();
	}



	CPoint3F CSurfaceNets::GetWorldSpaceSamplePosition(int x, int y, int z)
	{
		return sampleSpace.getMinPos() + CPoint3F(
			x * sampleSpace.size().x / sampleResolution().x,
			y * sampleSpace.size().y / sampleResolution().y,
			z * sampleSpace.size().z / sampleResolution().z);
	}

}